package genetic.q2;

import genetic.Genotype;
import genetic.Phenotype;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class PrisonerPhenotype extends Phenotype {

	private int _size;
	private List<Genotype> inspection = new LinkedList<Genotype>();

	public PrisonerPhenotype(){

	}

	public PrisonerPhenotype(int size){
		_size = size;
		inspection.add(new PrisonerInspectionGenotype(1));
		inspection.add(new PrisonerInspectionGenotype(2));
		inspection.add(new PrisonerInspectionGenotype(3));
	}

	@Override
	public List<Genotype> getInitialPopulation(int populationSize) {
		return getInitialRandomPopulation(populationSize);
	}

	@Override
	public Genotype createRandomInstance() {
		Genotype ins = new PrisonerGenotypeRandom(_size);
		return ins;
	}

	@Override
	public Genotype[] crossover(Genotype g1, Genotype g2) {
		PrisonerGenotypeRandom g3 = (PrisonerGenotypeRandom) g1.clone();
		PrisonerGenotypeRandom g4 = (PrisonerGenotypeRandom) g2.clone();
		Random r = new Random();

		for(int j=0; j<g4.instance.size(); j++){
			int spot = r.nextInt(g4.instance.elementAt(j).length);
			for(int i=0; i<spot; i++){
				int tmp = g4.instance.elementAt(j)[i];
				g4.instance.elementAt(j)[i] = g3.instance.elementAt(j)[i];
				g3.instance.elementAt(j)[i] = tmp;
			}
		}
		Genotype[] ans = {g3, g4};
		return ans;
	}

	@Override
	public double fitness(Genotype g) {
		double sc = ((double) ((PrisonerGenotypeRandom)g).score);
		double tot = ((double) ((PrisonerGenotypeRandom)g).total);
		double fitness = 0;
		if (tot!=0)
			fitness = sc/tot;
//		System.out.println("score = "+((PrisonerGenotypeRandom)g).score+" total = "+((PrisonerGenotypeRandom)g).total + " fitness = "+fitness);
	return fitness;
	}

	@Override
	public Genotype mutate(Genotype g, double Pm) {
		PrisonerGenotypeRandom ans = (PrisonerGenotypeRandom) g.clone();
		ans.instance = ((PrisonerGenotypeRandom)g).instance;
		for(int j=0; j<ans.instance.size(); j++){
			bitwiseMutation(ans.instance.elementAt(j), Pm, 3);
		}
		return ans;
	}

	@Override
	public List<Genotype> getMatingPool(List<Genotype> population,
			int populationSize) {
		List<Genotype> newpopulation = new LinkedList<Genotype>();
		for(int i=0; i<populationSize; i++){
			Random r = new Random();
			int first = r.nextInt(populationSize);
			int second = r.nextInt(populationSize);
			Genotype f = population.get(first);
			Genotype s = population.get(second);
			if(fitness(f) > fitness(s))
				newpopulation.add(f);
			else newpopulation.add(s);
		}
		return newpopulation;
	}

	public void runContest(List<Genotype> population){
		for(int i=0; i<population.size()-1; i++){
			for(int j=i; j<population.size(); j++){
				match(population.get(i),population.get(j));
			}
			for(int j=i; j<inspection.size(); j++){
				matchInspection(population.get(i),inspection.get(j));
			}
		}
//		for(int i=0; i<population.size(); i++){
//			PrisonerGenotypeRandom g= (PrisonerGenotypeRandom) population.get(i);
//			double sc = ((double) ((PrisonerGenotypeRandom)g).score);
//			double tot = ((double) ((PrisonerGenotypeRandom)g).total);
//			double fitness = 0;
//			if (tot!=0)
//				fitness = sc/tot;
//			System.out.println(i+" score = "+((PrisonerGenotypeRandom)g).score+" total = "+((PrisonerGenotypeRandom)g).total + " fitness = "+fitness);
//		}
	}

	private void match(Genotype g1, Genotype g2) {
		boolean flag=true;
		int turn = 0;
		boolean f,s;
		while(flag){
				f = ((PrisonerGenotypeRandom)g1).returnChoise(turn);
				s = ((PrisonerGenotypeRandom)g2).returnChoise(turn);
				((PrisonerGenotypeRandom)g1).update(turn,f,s);
				((PrisonerGenotypeRandom)g2).update(turn,s,f);
			Random r = new Random();
			if(r.nextDouble()<0.00346)
				flag=false;
			turn++;
		}
	}

	private void matchInspection(Genotype g1, Genotype g2) {
		boolean flag=true;
		int turn = 0;
		boolean f,s;
		while(flag){
				f = ((PrisonerGenotypeRandom)g1).returnChoise(turn);
				s = ((PrisonerInspectionGenotype)g2).returnChoise(turn);
				((PrisonerGenotypeRandom)g1).update(turn,f,s);
				((PrisonerInspectionGenotype)g2).update(turn,s,f);
			Random r = new Random();
			if(r.nextDouble()<0.00346)
				flag=false;
			turn++;
		}
	}
	@Override
	public void updateFitness(List<Genotype> population) {
		runContest(population);
	}

}
